/**
 * Copyright 2013 Joan Zapata
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sxjs.common.base.baseadapter;

import android.animation.Animator;
import android.content.Context;
import android.support.annotation.IntDef;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutParams;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.sxjs.common.base.baseadapter.animation.AlphaInAnimation;
import com.sxjs.common.base.baseadapter.animation.BaseAnimation;
import com.sxjs.common.base.baseadapter.animation.ScaleInAnimation;
import com.sxjs.common.base.baseadapter.animation.SlideInBottomAnimation;
import com.sxjs.common.base.baseadapter.animation.SlideInLeftAnimation;
import com.sxjs.common.base.baseadapter.animation.SlideInRightAnimation;
import com.sxjs.common.base.baseadapter.entity.IExpandable;
import com.sxjs.common.base.baseadapter.loadmore.LoadMoreView;
import com.sxjs.common.base.baseadapter.loadmore.SimpleLoadMoreView;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;


/**
 * https://github.com/CymChad/BaseRecyclerViewAdapterHelper
 */
public abstract class BaseQuickAdapter<T, K extends BaseViewHolder> extends RecyclerView.Adapter<K> {

	//load more
	private boolean mNextLoadEnable = false;
	private boolean mLoadMoreEnable = false;
	private boolean mLoading = false;
	private LoadMoreView mLoadMoreView = new SimpleLoadMoreView();
	private RequestLoadMoreListener mRequestLoadMoreListener;

	//Animation
	/**
	 * Use with {@link #openLoadAnimation}
	 */
	public static final int ALPHAIN = 0x00000001;
	/**
	 * Use with {@link #openLoadAnimation}
	 */
	public static final int SCALEIN = 0x00000002;
	/**
	 * Use with {@link #openLoadAnimation}
	 */
	public static final int SLIDEIN_BOTTOM = 0x00000003;
	/**
	 * Use with {@link #openLoadAnimation}
	 */
	public static final int SLIDEIN_LEFT = 0x00000004;
	/**
	 * Use with {@link #openLoadAnimation}
	 */
	public static final int SLIDEIN_RIGHT = 0x00000005;
	private OnItemClickListener mOnItemClickListener;
	private OnItemLongClickListener mOnItemLongClickListener;
	private OnItemChildClickListener mOnItemChildClickListener;
	private OnItemChildLongClickListener mOnItemChildLongClickListener;

	@IntDef({ALPHAIN, SCALEIN, SLIDEIN_BOTTOM, SLIDEIN_LEFT, SLIDEIN_RIGHT})
	@Retention(RetentionPolicy.SOURCE)
	public @interface AnimationType {
	}

	private boolean mFirstOnlyEnable = true;
	private boolean mOpenAnimationEnable = false;
	private Interpolator mInterpolator = new LinearInterpolator();
	private int mDuration = 300;
	private int mLastPosition = -1;

	private BaseAnimation mCustomAnimation;
	private BaseAnimation mSelectAnimation = new AlphaInAnimation();
	//header footer
	private LinearLayout mHeaderLayout;
	private LinearLayout mFooterLayout;
	//empty
	private FrameLayout mEmptyLayout;
	private boolean mIsUseEmpty = true;
	private boolean mHeadAndEmptyEnable;
	private boolean mFootAndEmptyEnable;

	protected static final String TAG = BaseQuickAdapter.class.getSimpleName();
	protected Context mContext;
	protected int mLayoutResId;
	protected LayoutInflater mLayoutInflater;
	protected List<T> mData;
	public static final int HEADER_VIEW = 0x00000111;
	public static final int LOADING_VIEW = 0x00000222;
	public static final int FOOTER_VIEW = 0x00000333;
	public static final int EMPTY_VIEW = 0x00000555;

	private RecyclerView mRecyclerView;

	protected RecyclerView getRecyclerView() {
		return mRecyclerView;
	}

	private void setRecyclerView(RecyclerView recyclerView) {
		mRecyclerView = recyclerView;
	}

	private void checkNotNull() {
		if (getRecyclerView() == null) {
			throw new RuntimeException("please bind recyclerView first!");
		}
	}

	/**
	 * same as recyclerView.setAdapter(), and save the instance of recyclerView
	 */
	public void bindToRecyclerView(RecyclerView recyclerView) {
		if (getRecyclerView() != null) {
			throw new RuntimeException("Don't bind twice");
		}
		setRecyclerView(recyclerView);
		getRecyclerView().setAdapter(this);
	}

	/**
	 * @see #setOnLoadMoreListener(RequestLoadMoreListener, RecyclerView)
	 * @deprecated This method is because it can lead to crash: always call this method while RecyclerView is computing a layout or scrolling.
	 * Please use {@link #setOnLoadMoreListener(RequestLoadMoreListener, RecyclerView)}
	 */
	@Deprecated
	public void setOnLoadMoreListener(RequestLoadMoreListener requestLoadMoreListener) {
		openLoadMore(requestLoadMoreListener);
	}

	private void openLoadMore(RequestLoadMoreListener requestLoadMoreListener) {
		this.mRequestLoadMoreListener = requestLoadMoreListener;
		mNextLoadEnable = true;
		mLoadMoreEnable = true;
		mLoading = false;
	}

	public void setOnLoadMoreListener(RequestLoadMoreListener requestLoadMoreListener, RecyclerView recyclerView) {
		openLoadMore(requestLoadMoreListener);
		if (getRecyclerView() == null) {
			setRecyclerView(recyclerView);
		}
	}

	/**
	 * bind recyclerView {@link #bindToRecyclerView(RecyclerView)} before use!
	 *
	 * @see #disableLoadMoreIfNotFullPage(RecyclerView)
	 */
	public void disableLoadMoreIfNotFullPage() {
		checkNotNull();
		disableLoadMoreIfNotFullPage(getRecyclerView());
	}

	/**
	 * check if full page after {@link #setNewData(List)}, if full, it will enable load more again.
	 *
	 * @param recyclerView your recyclerView
	 * @see #setNewData(List)
	 */
	public void disableLoadMoreIfNotFullPage(RecyclerView recyclerView) {
		if (recyclerView == null) return;
		RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
		if (manager == null) return;
		if (manager instanceof LinearLayoutManager) {
			final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;
			recyclerView.postDelayed(new Runnable() {
				@Override
				public void run() {
					if ((linearLayoutManager.findLastCompletelyVisibleItemPosition() + 1) != getItemCount()) {
						setEnableLoadMore(true);
					}
				}
			}, 50);
		} else if (manager instanceof StaggeredGridLayoutManager) {
			final StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) manager;
			recyclerView.postDelayed(new Runnable() {
				@Override
				public void run() {
					final int[] positions = new int[staggeredGridLayoutManager.getSpanCount()];
					staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(positions);
					int pos = getTheBiggestNumber(positions) + 1;
					if (pos != getItemCount()) {
						setEnableLoadMore(true);
					}
				}
			}, 50);
		}
	}

	private int getTheBiggestNumber(int[] numbers) {
		int tmp = -1;
		if (numbers == null || numbers.length == 0) {
			return tmp;
		}
		for (int num : numbers) {
			if (num > tmp) {
				tmp = num;
			}
		}
		return tmp;
	}

	public void setNotDoAnimationCount(int count) {
		mLastPosition = count;
	}

	/**
	 * Set custom load more
	 *
	 * @param loadingView
	 */
	public void setLoadMoreView(LoadMoreView loadingView) {
		this.mLoadMoreView = loadingView;
	}

	/**
	 * Load more view count
	 *
	 * @return 0 or 1
	 */
	public int getLoadMoreViewCount() {
		if (mRequestLoadMoreListener == null || !mLoadMoreEnable) {
			return 0;
		}
		if (!mNextLoadEnable && mLoadMoreView.isLoadEndMoreGone()) {
			return 0;
		}
		if (mData.size() == 0) {
			return 0;
		}
		return 1;
	}

	/**
	 * @return Whether the Adapter is actively showing load
	 * progress.
	 */
	public boolean isLoading() {
		return mLoading;
	}


	/**
	 * Refresh end, no more data
	 */
	public void loadMoreEnd() {
		loadMoreEnd(false);
	}

	/**
	 * Refresh end, no more data
	 *
	 * @param gone if true gone the load more view
	 */
	public void loadMoreEnd(boolean gone) {
		if (getLoadMoreViewCount() == 0) {
			return;
		}
		mLoading = false;
		mNextLoadEnable = false;
		mLoadMoreView.setLoadMoreEndGone(gone);
		if (gone) {
			notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
		} else {
			mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_END);
			notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
		}
	}

	/**
	 * Refresh complete
	 */
	public void loadMoreComplete() {
		if (getLoadMoreViewCount() == 0) {
			return;
		}
		mLoading = false;
		mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
		notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
	}

	/**
	 * Refresh failed
	 */
	public void loadMoreFail() {
		if (getLoadMoreViewCount() == 0) {
			return;
		}
		mLoading = false;
		mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_FAIL);
		notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
	}

	/**
	 * Set the enabled state of load more.
	 *
	 * @param enable True if load more is enabled, false otherwise.
	 */
	public void setEnableLoadMore(boolean enable) {
		int oldLoadMoreCount = getLoadMoreViewCount();
		mLoadMoreEnable = enable;
		int newLoadMoreCount = getLoadMoreViewCount();

		if (oldLoadMoreCount == 1) {
			if (newLoadMoreCount == 0) {
				notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
			}
		} else {
			if (newLoadMoreCount == 1) {
				mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
				notifyItemInserted(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
			}
		}
	}

	/**
	 * Returns the enabled status for load more.
	 *
	 * @return True if load more is enabled, false otherwise.
	 */
	public boolean isLoadMoreEnable() {
		return mLoadMoreEnable;
	}

	/**
	 * Sets the duration of the animation.
	 *
	 * @param duration The length of the animation, in milliseconds.
	 */
	public void setDuration(int duration) {
		mDuration = duration;
	}


	/**
	 * Same as QuickAdapter#QuickAdapter(Context,int) but with
	 * some initialization data.
	 *
	 * @param layoutResId The layout resource id of each item.
	 * @param data        A new list is created out of this one to avoid mutable list
	 */
	public BaseQuickAdapter(int layoutResId, List<T> data) {
		this.mData = data == null ? new ArrayList<T>() : data;
		if (layoutResId != 0) {
			this.mLayoutResId = layoutResId;
		}
	}

	public BaseQuickAdapter(List<T> data) {
		this(0, data);
	}

	public BaseQuickAdapter(int layoutResId) {
		this(layoutResId, null);
	}

	public BaseQuickAdapter() {
		this(0, null);
	}

	/**
	 * setting up a new instance to data;
	 *
	 * @param data
	 */
	public void setNewData(List<T> data) {
		this.mData = data == null ? new ArrayList<T>() : data;
		if (mRequestLoadMoreListener != null) {
			mNextLoadEnable = true;
			mLoadMoreEnable = true;
			mLoading = false;
			mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
		}
		mLastPosition = -1;
		notifyDataSetChanged();
	}


	/**
	 * insert  a item associated with the specified position of adapter
	 *
	 * @param position
	 * @param item
	 * @deprecated use {@link #addData(int, Object)} instead
	 */
	@Deprecated
	public void add(int position, T item) {
		addData(position, item);
	}

	/**
	 * add one new data in to certain location
	 *
	 * @param position
	 */
	public void addData(int position, T data) {
		mData.add(position, data);
		notifyItemInserted(position + getHeaderLayoutCount());
		compatibilityDataSizeChanged(1);
	}

	/**
	 * add one new data
	 */
	public void addData(T data) {
		mData.add(data);
		notifyItemInserted(mData.size() + getHeaderLayoutCount());
		compatibilityDataSizeChanged(1);
	}

	/**
	 * remove the item associated with the specified position of adapter
	 *
	 * @param position
	 */
	public void remove(int position) {
		mData.remove(position);
		notifyItemRemoved(position + getHeaderLayoutCount());
		compatibilityDataSizeChanged(0);
		notifyItemRangeChanged(position, mData.size() - position);
	}

	/**
	 * change data
	 */
	public void setData(int index, T data) {
		mData.set(index, data);
		notifyItemChanged(index + getHeaderLayoutCount());
	}

	/**
	 * add new data in to certain location
	 *
	 * @param position
	 */
	public void addData(int position, List<T> data) {
		mData.addAll(position, data);
		notifyItemRangeInserted(position + getHeaderLayoutCount(), data.size());
		compatibilityDataSizeChanged(data.size());
	}

	/**
	 * additional data;
	 *
	 * @param newData
	 */
	public void addData(List<T> newData) {
		this.mData.addAll(newData);
		notifyItemRangeInserted(mData.size() - newData.size() + getHeaderLayoutCount(), newData.size());
		compatibilityDataSizeChanged(newData.size());
	}

	/**
	 * compatible getLoadMoreViewCount and getEmptyViewCount may change
	 *
	 * @param size Need compatible data size
	 */
	private void compatibilityDataSizeChanged(int size) {
		final int dataSize = mData == null ? 0 : mData.size();
		if (dataSize == size) {
			notifyDataSetChanged();
		}
	}

	/**
	 * Get the data of list
	 *
	 * @return
	 */
	public List<T> getData() {
		return mData;
	}

	/**
	 * Get the data item associated with the specified position in the data set.
	 *
	 * @param position Position of the item whose data we want within the adapter's
	 *                 data set.
	 * @return The data at the specified position.
	 */
	public T getItem(int position) {
		if (position != -1)
			return mData.get(position);
		else
			return null;
	}

	/**
	 * if setHeadView will be return 1 if not will be return 0.
	 * notice: Deprecated! Use {@link ViewGroup#getChildCount()} of {@link #getHeaderLayout()} to replace.
	 *
	 * @return
	 */
	@Deprecated
	public int getHeaderViewsCount() {
		return getHeaderLayoutCount();
	}

	/**
	 * if mFooterLayout will be return 1 or not will be return 0.
	 * notice: Deprecated! Use {@link ViewGroup#getChildCount()} of {@link #getFooterLayout()} to replace.
	 *
	 * @return
	 */
	@Deprecated
	public int getFooterViewsCount() {
		return getFooterLayoutCount();
	}

	/**
	 * if addHeaderView will be return 1, if not will be return 0
	 */
	public int getHeaderLayoutCount() {
		if (mHeaderLayout == null || mHeaderLayout.getChildCount() == 0) {
			return 0;
		}
		return 1;
	}

	/**
	 * if addFooterView will be return 1, if not will be return 0
	 */
	public int getFooterLayoutCount() {
		if (mFooterLayout == null || mFooterLayout.getChildCount() == 0) {
			return 0;
		}
		return 1;
	}

	/**
	 * if show empty view will be return 1 or not will be return 0
	 *
	 * @return
	 */
	public int getEmptyViewCount() {
		if (mEmptyLayout == null || mEmptyLayout.getChildCount() == 0) {
			return 0;
		}
		if (!mIsUseEmpty) {
			return 0;
		}
		if (mData.size() != 0) {
			return 0;
		}
		return 1;
	}

	@Override
	public int getItemCount() {
		int count;
		if (getEmptyViewCount() == 1) {
			count = 1;
			if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
				count++;
			}
			if (mFootAndEmptyEnable && getFooterLayoutCount() != 0) {
				count++;
			}
		} else {
			count = getHeaderLayoutCount() + mData.size() + getFooterLayoutCount() + getLoadMoreViewCount();
		}
		return count;
	}

	@Override
	public int getItemViewType(int position) {
		if (getEmptyViewCount() == 1) {
			boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
			switch (position) {
				case 0:
					if (header) {
						return HEADER_VIEW;
					} else {
						return EMPTY_VIEW;
					}
				case 1:
					if (header) {
						return EMPTY_VIEW;
					} else {
						return FOOTER_VIEW;
					}
				case 2:
					return FOOTER_VIEW;
				default:
					return EMPTY_VIEW;
			}
		}
		autoLoadMore(position);
		int numHeaders = getHeaderLayoutCount();
		if (position < numHeaders) {
			return HEADER_VIEW;
		} else {
			int adjPosition = position - numHeaders;
			int adapterCount = mData.size();
			if (adjPosition < adapterCount) {
				return getDefItemViewType(adjPosition);
			} else {
				adjPosition = adjPosition - adapterCount;
				int numFooters = getFooterLayoutCount();
				if (adjPosition < numFooters) {
					return FOOTER_VIEW;
				} else {
					return LOADING_VIEW;
				}
			}
		}
	}

	protected int getDefItemViewType(int position) {
		return super.getItemViewType(position);
	}

	@Override
	public K onCreateViewHolder(ViewGroup parent, int viewType) {
		K baseViewHolder = null;
		this.mContext = parent.getContext();
		this.mLayoutInflater = LayoutInflater.from(mContext);
		switch (viewType) {
			case LOADING_VIEW:
				baseViewHolder = getLoadingView(parent);
				break;
			case HEADER_VIEW:
				baseViewHolder = createBaseViewHolder(mHeaderLayout);
				break;
			case EMPTY_VIEW:
				baseViewHolder = createBaseViewHolder(mEmptyLayout);
				break;
			case FOOTER_VIEW:
				baseViewHolder = createBaseViewHolder(mFooterLayout);
				break;
			default:
				baseViewHolder = onCreateDefViewHolder(parent, viewType);
				bindViewClickListener(baseViewHolder);
		}
		baseViewHolder.setAdapter(this);
		return baseViewHolder;

	}


	private K getLoadingView(ViewGroup parent) {
		View view = getItemView(mLoadMoreView.getLayoutId(), parent);
		K holder = createBaseViewHolder(view);
		holder.itemView.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if (mLoadMoreView.getLoadMoreStatus() == LoadMoreView.STATUS_FAIL) {
					mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
					notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
				}
			}
		});
		return holder;
	}

	/**
	 * Called when a view created by this adapter has been attached to a window.
	 * simple to solve item will layout using all
	 * {@link #setFullSpan(RecyclerView.ViewHolder)}
	 *
	 * @param holder
	 */
	@Override
	public void onViewAttachedToWindow(K holder) {
		super.onViewAttachedToWindow(holder);
		int type = holder.getItemViewType();
		if (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW) {
			setFullSpan(holder);
		} else {
			addAnimation(holder);
		}
	}

	/**
	 * When set to true, the item will layout using all span area. That means, if orientation
	 * is vertical, the view will have full width; if orientation is horizontal, the view will
	 * have full height.
	 * if the hold view use StaggeredGridLayoutManager they should using all span area
	 *
	 * @param holder True if this item should traverse all spans.
	 */
	protected void setFullSpan(RecyclerView.ViewHolder holder) {
		if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
			StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder
					.itemView.getLayoutParams();
			params.setFullSpan(true);
		}
	}

	@Override
	public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
		super.onAttachedToRecyclerView(recyclerView);
		RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
		if (manager instanceof GridLayoutManager) {
			final GridLayoutManager gridManager = ((GridLayoutManager) manager);
			gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
				@Override
				public int getSpanSize(int position) {
					int type = getItemViewType(position);
					if (mSpanSizeLookup == null)
						return (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type ==
								LOADING_VIEW) ? gridManager.getSpanCount() : 1;
					else
						return (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type ==
								LOADING_VIEW) ? gridManager.getSpanCount() : mSpanSizeLookup.getSpanSize(gridManager,
								position - getHeaderLayoutCount());
				}
			});
		}
	}

	private SpanSizeLookup mSpanSizeLookup;

	public interface SpanSizeLookup {
		int getSpanSize(GridLayoutManager gridLayoutManager, int position);
	}

	/**
	 * @param spanSizeLookup instance to be used to query number of spans occupied by each item
	 */
	public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
		this.mSpanSizeLookup = spanSizeLookup;
	}

	/**
	 * To bind different types of holder and solve different the bind events
	 *
	 * @param holder
	 * @param positions
	 * @see #getDefItemViewType(int)
	 */
	@Override
	public void onBindViewHolder(K holder, int positions) {
		int viewType = holder.getItemViewType();

		switch (viewType) {
			case 0:

				convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()), holder.getLayoutPosition() - getHeaderLayoutCount());
				break;
			case LOADING_VIEW:
				mLoadMoreView.convert(holder);
				break;
			case HEADER_VIEW:
				break;
			case EMPTY_VIEW:
				break;
			case FOOTER_VIEW:
				break;
			default:
				convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()), holder.getLayoutPosition() - getHeaderLayoutCount());
				break;
		}
	}

	private void bindViewClickListener(final BaseViewHolder baseViewHolder) {
		if (baseViewHolder == null) {
			return;
		}
		final View view = baseViewHolder.getConvertView();
		if (view == null) {
			return;
		}
		view.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if (getOnItemClickListener() != null && baseViewHolder != null) {

					getOnItemClickListener().onItemClick(BaseQuickAdapter.this, v, baseViewHolder.getLayoutPosition() - getHeaderLayoutCount());
				}

			}
		});
		view.setOnLongClickListener(new View.OnLongClickListener() {
			@Override
			public boolean onLongClick(View v) {
				if (getOnItemLongClickListener() != null && baseViewHolder != null) {
					return getOnItemLongClickListener().onItemLongClick(BaseQuickAdapter.this, v, baseViewHolder.getLayoutPosition() - getHeaderLayoutCount());
				}
				return false;

			}
		});
	}

	protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
		return createBaseViewHolder(parent, mLayoutResId);
	}

	protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
		return createBaseViewHolder(getItemView(layoutResId, parent));
	}

	/**
	 * if you want to use subclass of BaseViewHolder in the adapter,
	 * you must override the method to create new ViewHolder.
	 *
	 * @param view view
	 * @return new ViewHolder
	 */
	protected K createBaseViewHolder(View view) {
		Class temp = getClass();
		Class z = null;
		while (z == null && null != temp) {
			z = getInstancedGenericKClass(temp);
			temp = temp.getSuperclass();
		}
		K k = createGenericKInstance(z, view);
		return null != k ? k : (K) new BaseViewHolder(view);
	}

	/**
	 * try to create Generic K instance
	 *
	 * @param z
	 * @param view
	 * @return
	 */
	private K createGenericKInstance(Class z, View view) {
		try {
			Constructor constructor;
			String buffer = Modifier.toString(z.getModifiers());
			String className = z.getName();
			// inner and unstatic class
			if (className.contains("$") && !buffer.contains("static")) {
				constructor = z.getDeclaredConstructor(getClass(), View.class);
				return (K) constructor.newInstance(this, view);
			} else {
				constructor = z.getDeclaredConstructor(View.class);
				return (K) constructor.newInstance(view);
			}
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * get generic parameter K
	 *
	 * @param z
	 * @return
	 */
	private Class getInstancedGenericKClass(Class z) {
		Type type = z.getGenericSuperclass();
		if (type instanceof ParameterizedType) {
			Type[] types = ((ParameterizedType) type).getActualTypeArguments();
			for (Type temp : types) {
				if (temp instanceof Class) {
					Class tempClass = (Class) temp;
					if (BaseViewHolder.class.isAssignableFrom(tempClass)) {
						return tempClass;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Return root layout of header
	 */

	public LinearLayout getHeaderLayout() {
		return mHeaderLayout;
	}

	/**
	 * Return root layout of footer
	 */
	public LinearLayout getFooterLayout() {
		return mFooterLayout;
	}

	/**
	 * Append header to the rear of the mHeaderLayout.
	 *
	 * @param header
	 */
	public int addHeaderView(View header) {
		return addHeaderView(header, -1);
	}

	/**
	 * Add header view to mHeaderLayout and set header view position in mHeaderLayout.
	 * When index = -1 or index >= child count in mHeaderLayout,
	 * the effect of this method is the same as that of {@link #addHeaderView(View)}.
	 *
	 * @param header
	 * @param index  the position in mHeaderLayout of this header.
	 *               When index = -1 or index >= child count in mHeaderLayout,
	 *               the effect of this method is the same as that of {@link #addHeaderView(View)}.
	 */
	public int addHeaderView(View header, int index) {
		return addHeaderView(header, index, LinearLayout.VERTICAL);
	}

	/**
	 * @param header
	 * @param index
	 * @param orientation
	 */
	public int addHeaderView(View header, int index, int orientation) {
		if (mHeaderLayout == null) {
			mHeaderLayout = new LinearLayout(header.getContext());
			if (orientation == LinearLayout.VERTICAL) {
				mHeaderLayout.setOrientation(LinearLayout.VERTICAL);
				mHeaderLayout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
			} else {
				mHeaderLayout.setOrientation(LinearLayout.HORIZONTAL);
				mHeaderLayout.setLayoutParams(new LayoutParams(WRAP_CONTENT, MATCH_PARENT));
			}
		}
		final int childCount = mHeaderLayout.getChildCount();
		if (index < 0 || index > childCount) {
			index = childCount;
		}
		mHeaderLayout.addView(header, index);
		if (mHeaderLayout.getChildCount() == 1) {
			int position = getHeaderViewPosition();
			if (position != -1) {
				notifyItemInserted(position);
			}
		}
		return index;
	}

	public int setHeaderView(View header) {
		return setHeaderView(header, 0, LinearLayout.VERTICAL);
	}

	public int setHeaderView(View header, int index) {
		return setHeaderView(header, index, LinearLayout.VERTICAL);
	}

	public int setHeaderView(View header, int index, int orientation) {
		if (mHeaderLayout == null || mHeaderLayout.getChildCount() <= index) {
			return addHeaderView(header, index, orientation);
		} else {
			mHeaderLayout.removeViewAt(index);
			mHeaderLayout.addView(header, index);
			return index;
		}
	}

	/**
	 * Append footer to the rear of the mFooterLayout.
	 *
	 * @param footer
	 */
	public int addFooterView(View footer) {
		return addFooterView(footer, -1, LinearLayout.VERTICAL);
	}

	public int addFooterView(View footer, int index) {
		return addFooterView(footer, index, LinearLayout.VERTICAL);
	}

	/**
	 * Add footer view to mFooterLayout and set footer view position in mFooterLayout.
	 * When index = -1 or index >= child count in mFooterLayout,
	 * the effect of this method is the same as that of {@link #addFooterView(View)}.
	 *
	 * @param footer
	 * @param index  the position in mFooterLayout of this footer.
	 *               When index = -1 or index >= child count in mFooterLayout,
	 *               the effect of this method is the same as that of {@link #addFooterView(View)}.
	 */
	public int addFooterView(View footer, int index, int orientation) {
		if (mFooterLayout == null) {
			mFooterLayout = new LinearLayout(footer.getContext());
			if (orientation == LinearLayout.VERTICAL) {
				mFooterLayout.setOrientation(LinearLayout.VERTICAL);
				mFooterLayout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
			} else {
				mFooterLayout.setOrientation(LinearLayout.HORIZONTAL);
				mFooterLayout.setLayoutParams(new LayoutParams(WRAP_CONTENT, MATCH_PARENT));
			}
		}
		final int childCount = mFooterLayout.getChildCount();
		if (index < 0 || index > childCount) {
			index = childCount;
		}
		mFooterLayout.addView(footer, index);
		if (mFooterLayout.getChildCount() == 1) {
			int position = getFooterViewPosition();
			if (position != -1) {
				notifyItemInserted(position);
			}
		}
		return index;
	}

	public int setFooterView(View header) {
		return setFooterView(header, 0, LinearLayout.VERTICAL);
	}

	public int setFooterView(View header, int index) {
		return setFooterView(header, index, LinearLayout.VERTICAL);
	}

	public int setFooterView(View header, int index, int orientation) {
		if (mFooterLayout == null || mFooterLayout.getChildCount() <= index) {
			return addFooterView(header, index, orientation);
		} else {
			mFooterLayout.removeViewAt(index);
			mFooterLayout.addView(header, index);
			return index;
		}
	}

	/**
	 * remove header view from mHeaderLayout.
	 * When the child count of mHeaderLayout is 0, mHeaderLayout will be set to null.
	 *
	 * @param header
	 */
	public void removeHeaderView(View header) {
		if (getHeaderLayoutCount() == 0) return;

		mHeaderLayout.removeView(header);
		if (mHeaderLayout.getChildCount() == 0) {
			int position = getHeaderViewPosition();
			if (position != -1) {
				notifyItemRemoved(position);
			}
		}
	}

	/**
	 * remove footer view from mFooterLayout,
	 * When the child count of mFooterLayout is 0, mFooterLayout will be set to null.
	 *
	 * @param footer
	 */
	public void removeFooterView(View footer) {
		if (getFooterLayoutCount() == 0) return;

		mFooterLayout.removeView(footer);
		if (mFooterLayout.getChildCount() == 0) {
			int position = getFooterViewPosition();
			if (position != -1) {
				notifyItemRemoved(position);
			}
		}
	}

	/**
	 * remove all header view from mHeaderLayout and set null to mHeaderLayout
	 */
	public void removeAllHeaderView() {
		if (getHeaderLayoutCount() == 0) return;

		mHeaderLayout.removeAllViews();
		int position = getHeaderViewPosition();
		if (position != -1) {
			notifyItemRemoved(position);
		}
	}

	/**
	 * remove all footer view from mFooterLayout and set null to mFooterLayout
	 */
	public void removeAllFooterView() {
		if (getFooterLayoutCount() == 0) return;

		mFooterLayout.removeAllViews();
		int position = getFooterViewPosition();
		if (position != -1) {
			notifyItemRemoved(position);
		}
	}

	private int getHeaderViewPosition() {
		//Return to header view notify position
		if (getEmptyViewCount() == 1) {
			if (mHeadAndEmptyEnable) {
				return 0;
			}
		} else {
			return 0;
		}
		return -1;
	}

	private int getFooterViewPosition() {
		//Return to footer view notify position
		if (getEmptyViewCount() == 1) {
			int position = 1;
			if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
				position++;
			}
			if (mFootAndEmptyEnable) {
				return position;
			}
		} else {
			return getHeaderLayoutCount() + mData.size();
		}
		return -1;
	}

	public void setEmptyView(int layoutResId, ViewGroup viewGroup) {
		View view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutResId, viewGroup, false);
		setEmptyView(view);
	}

	/**
	 * bind recyclerView {@link #bindToRecyclerView(RecyclerView)} before use!
	 *
	 * @see #bindToRecyclerView(RecyclerView)
	 */
	public void setEmptyView(int layoutResId) {
		checkNotNull();
		setEmptyView(layoutResId, getRecyclerView());
	}

	public void setEmptyView(View emptyView) {
		boolean insert = false;
		if (mEmptyLayout == null) {
			mEmptyLayout = new FrameLayout(emptyView.getContext());
			final LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
			final ViewGroup.LayoutParams lp = emptyView.getLayoutParams();
			if (lp != null) {
				layoutParams.width = lp.width;
				layoutParams.height = lp.height;
			}
			mEmptyLayout.setLayoutParams(layoutParams);
			insert = true;
		}
		mEmptyLayout.removeAllViews();
		mEmptyLayout.addView(emptyView);
		mIsUseEmpty = true;
		if (insert) {
			if (getEmptyViewCount() == 1) {
				int position = 0;
				if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
					position++;
				}
				notifyItemInserted(position);
			}
		}
	}

	/**
	 * Call before {@link RecyclerView#setAdapter(RecyclerView.Adapter)}
	 *
	 * @param isHeadAndEmpty false will not show headView if the data is empty true will show emptyView and headView
	 */
	public void setHeaderAndEmpty(boolean isHeadAndEmpty) {
		setHeaderFooterEmpty(isHeadAndEmpty, false);
	}

	/**
	 * set emptyView show if adapter is empty and want to show headview and footview
	 * Call before {@link RecyclerView#setAdapter(RecyclerView.Adapter)}
	 *
	 * @param isHeadAndEmpty
	 * @param isFootAndEmpty
	 */
	public void setHeaderFooterEmpty(boolean isHeadAndEmpty, boolean isFootAndEmpty) {
		mHeadAndEmptyEnable = isHeadAndEmpty;
		mFootAndEmptyEnable = isFootAndEmpty;
	}

	/**
	 * Set whether to use empty view
	 *
	 * @param isUseEmpty
	 */
	public void isUseEmpty(boolean isUseEmpty) {
		mIsUseEmpty = isUseEmpty;
	}

	/**
	 * When the current adapter is empty, the BaseQuickAdapter can display a special view
	 * called the empty view. The empty view is used to provide feedback to the user
	 * that no data is available in this AdapterView.
	 *
	 * @return The view to show if the adapter is empty.
	 */
	public View getEmptyView() {
		return mEmptyLayout;
	}

	private int mAutoLoadMoreSize = 1;

	public void setAutoLoadMoreSize(int autoLoadMoreSize) {
		if (autoLoadMoreSize > 1) {
			mAutoLoadMoreSize = autoLoadMoreSize;
		}
	}

	private void autoLoadMore(int position) {
		if (getLoadMoreViewCount() == 0) {
			return;
		}
		if (position < getItemCount() - mAutoLoadMoreSize) {
			return;
		}
		if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) {
			return;
		}
		mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING);
		if (!mLoading) {
			mLoading = true;
			if (getRecyclerView() != null) {
				getRecyclerView().post(new Runnable() {
					@Override
					public void run() {
						mRequestLoadMoreListener.onLoadMoreRequested();
					}
				});
			} else {
				mRequestLoadMoreListener.onLoadMoreRequested();
			}
		}
	}


	/**
	 * add animation when you want to show time
	 *
	 * @param holder
	 */
	private void addAnimation(RecyclerView.ViewHolder holder) {
		if (mOpenAnimationEnable) {
			if (!mFirstOnlyEnable || holder.getLayoutPosition() > mLastPosition) {
				BaseAnimation animation = null;
				if (mCustomAnimation != null) {
					animation = mCustomAnimation;
				} else {
					animation = mSelectAnimation;
				}
				for (Animator anim : animation.getAnimators(holder.itemView)) {
					startAnim(anim, holder.getLayoutPosition());
				}
				mLastPosition = holder.getLayoutPosition();
			}
		}
	}

	/**
	 * set anim to start when loading
	 *
	 * @param anim
	 * @param index
	 */
	protected void startAnim(Animator anim, int index) {
		anim.setDuration(mDuration).start();
		anim.setInterpolator(mInterpolator);
	}

	/**
	 * @param layoutResId ID for an XML layout resource to load
	 * @param parent      Optional view to be the parent of the generated hierarchy or else simply an object that
	 *                    provides a set of LayoutParams values for root of the returned
	 *                    hierarchy
	 * @return view will be return
	 */
	protected View getItemView(int layoutResId, ViewGroup parent) {
		return mLayoutInflater.inflate(layoutResId, parent, false);
	}


	public interface RequestLoadMoreListener {

		void onLoadMoreRequested();

	}


	/**
	 * Set the view animation type.
	 *
	 * @param animationType One of {@link #ALPHAIN}, {@link #SCALEIN}, {@link #SLIDEIN_BOTTOM},
	 *                      {@link #SLIDEIN_LEFT}, {@link #SLIDEIN_RIGHT}.
	 */
	public void openLoadAnimation(@AnimationType int animationType) {
		this.mOpenAnimationEnable = true;
		mCustomAnimation = null;
		switch (animationType) {
			case ALPHAIN:
				mSelectAnimation = new AlphaInAnimation();
				break;
			case SCALEIN:
				mSelectAnimation = new ScaleInAnimation();
				break;
			case SLIDEIN_BOTTOM:
				mSelectAnimation = new SlideInBottomAnimation();
				break;
			case SLIDEIN_LEFT:
				mSelectAnimation = new SlideInLeftAnimation();
				break;
			case SLIDEIN_RIGHT:
				mSelectAnimation = new SlideInRightAnimation();
				break;
			default:
				break;
		}
	}

	/**
	 * Set Custom ObjectAnimator
	 *
	 * @param animation ObjectAnimator
	 */
	public void openLoadAnimation(BaseAnimation animation) {
		this.mOpenAnimationEnable = true;
		this.mCustomAnimation = animation;
	}

	/**
	 * To open the animation when loading
	 */
	public void openLoadAnimation() {
		this.mOpenAnimationEnable = true;
	}

	/**
	 * {@link #addAnimation(RecyclerView.ViewHolder)}
	 *
	 * @param firstOnly true just show anim when first loading false show anim when load the data every time
	 */
	public void isFirstOnly(boolean firstOnly) {
		this.mFirstOnlyEnable = firstOnly;
	}

	/**
	 * Implement this method and use the helper to adapt the view to the given item.
	 *
	 * @param helper A fully initialized helper.
	 * @param item   The item that needs to be displayed.
	 */
	protected abstract void convert(K helper, T item, int position);

	/**
	 * get the specific view by position,e.g. getViewByPosition(2, R.id.textView)
	 * <p>
	 * bind recyclerView {@link #bindToRecyclerView(RecyclerView)} before use!
	 *
	 * @see #bindToRecyclerView(RecyclerView)
	 */
	public View getViewByPosition(int position, int viewId) {
		checkNotNull();
		return getViewByPosition(getRecyclerView(), position, viewId);
	}

	public View getViewByPosition(RecyclerView recyclerView, int position, int viewId) {
		if (recyclerView == null) {
			return null;
		}
		BaseViewHolder viewHolder = (BaseViewHolder) recyclerView.findViewHolderForLayoutPosition(position);
		return viewHolder.getView(viewId);
	}

	/**
	 * Get the row id associated with the specified position in the list.
	 *
	 * @param position The position of the item within the adapter's data set whose row id we want.
	 * @return The id of the item at the specified position.
	 */
	@Override
	public long getItemId(int position) {
		return position;
	}

	private int recursiveExpand(int position, @NonNull List list) {
		int count = 0;
		int pos = position + list.size() - 1;
		for (int i = list.size() - 1; i >= 0; i--, pos--) {
			if (list.get(i) instanceof IExpandable) {
				IExpandable item = (IExpandable) list.get(i);
				if (item.isExpanded() && hasSubItems(item)) {
					List subList = item.getSubItems();
					mData.addAll(pos + 1, subList);
					int subItemCount = recursiveExpand(pos + 1, subList);
					count += subItemCount;
				}
			}
		}
		return count;

	}

	/**
	 * Expand an expandable item
	 *
	 * @param position     position of the item
	 * @param animate      expand items with animation
	 * @param shouldNotify notify the RecyclerView to rebind items, <strong>false</strong> if you want to do it
	 *                     yourself.
	 * @return the number of items that have been added.
	 */
	public int expand(@IntRange(from = 0) int position, boolean animate, boolean shouldNotify) {
		position -= getHeaderLayoutCount();

		IExpandable expandable = getExpandableItem(position);
		if (expandable == null) {
			return 0;
		}
		if (!hasSubItems(expandable)) {
			expandable.setExpanded(false);
			return 0;
		}
		int subItemCount = 0;
		if (!expandable.isExpanded()) {
			List list = expandable.getSubItems();
			mData.addAll(position + 1, list);
			subItemCount += recursiveExpand(position + 1, list);

			expandable.setExpanded(true);
			subItemCount += list.size();
		}
		int parentPos = position + getHeaderLayoutCount();
		if (shouldNotify) {
			if (animate) {
				notifyItemChanged(parentPos);
				notifyItemRangeInserted(parentPos + 1, subItemCount);
			} else {
				notifyDataSetChanged();
			}
		}
		return subItemCount;
	}

	/**
	 * Expand an expandable item
	 *
	 * @param position position of the item, which includes the header layout count.
	 * @param animate  expand items with animation
	 * @return the number of items that have been added.
	 */
	public int expand(@IntRange(from = 0) int position, boolean animate) {
		return expand(position, animate, true);
	}

	/**
	 * Expand an expandable item with animation.
	 *
	 * @param position position of the item, which includes the header layout count.
	 * @return the number of items that have been added.
	 */
	public int expand(@IntRange(from = 0) int position) {
		return expand(position, true, true);
	}

	public int expandAll(int position, boolean animate, boolean notify) {
		position -= getHeaderLayoutCount();

		T endItem = null;
		if (position + 1 < this.mData.size()) {
			endItem = getItem(position + 1);
		}

		IExpandable expandable = getExpandableItem(position);
		if (!hasSubItems(expandable)) {
			return 0;
		}

		int count = expand(position + getHeaderLayoutCount(), false, false);
		for (int i = position + 1; i < this.mData.size(); i++) {
			T item = getItem(i);

			if (item == endItem) {
				break;
			}
			if (isExpandable(item)) {
				count += expand(i + getHeaderLayoutCount(), false, false);
			}
		}

		if (notify) {
			if (animate) {
				notifyItemRangeInserted(position + getHeaderLayoutCount() + 1, count);
			} else {
				notifyDataSetChanged();
			}
		}
		return count;
	}

	/**
	 * expand the item and all its subItems
	 *
	 * @param position position of the item, which includes the header layout count.
	 * @param init     whether you are initializing the recyclerView or not.
	 *                 if <strong>true</strong>, it won't notify recyclerView to redraw UI.
	 * @return the number of items that have been added to the adapter.
	 */
	public int expandAll(int position, boolean init) {
		return expandAll(position, true, !init);
	}

	private int recursiveCollapse(@IntRange(from = 0) int position) {
		T item = getItem(position);
		if (!isExpandable(item)) {
			return 0;
		}
		IExpandable expandable = (IExpandable) item;
		int subItemCount = 0;
		if (expandable.isExpanded()) {
			List<T> subItems = expandable.getSubItems();
			for (int i = subItems.size() - 1; i >= 0; i--) {
				T subItem = subItems.get(i);
				int pos = getItemPosition(subItem);
				if (pos < 0) {
					continue;
				}
				if (subItem instanceof IExpandable) {
					subItemCount += recursiveCollapse(pos);
				}
				mData.remove(pos);
				subItemCount++;
			}
		}
		return subItemCount;
	}

	/**
	 * Collapse an expandable item that has been expanded..
	 *
	 * @param position the position of the item, which includes the header layout count.
	 * @param animate  collapse with animation or not.
	 * @param notify   notify the recyclerView refresh UI or not.
	 * @return the number of subItems collapsed.
	 */
	public int collapse(@IntRange(from = 0) int position, boolean animate, boolean notify) {
		position -= getHeaderLayoutCount();

		IExpandable expandable = getExpandableItem(position);
		if (expandable == null) {
			return 0;
		}
		int subItemCount = recursiveCollapse(position);
		expandable.setExpanded(false);
		int parentPos = position + getHeaderLayoutCount();
		if (notify) {
			if (animate) {
				notifyItemChanged(parentPos);
				notifyItemRangeRemoved(parentPos + 1, subItemCount);
			} else {
				notifyDataSetChanged();
			}
		}
		return subItemCount;
	}

	/**
	 * Collapse an expandable item that has been expanded..
	 *
	 * @param position the position of the item, which includes the header layout count.
	 * @return the number of subItems collapsed.
	 */
	public int collapse(@IntRange(from = 0) int position) {
		return collapse(position, true, true);
	}

	/**
	 * Collapse an expandable item that has been expanded..
	 *
	 * @param position the position of the item, which includes the header layout count.
	 * @return the number of subItems collapsed.
	 */
	public int collapse(@IntRange(from = 0) int position, boolean animate) {
		return collapse(position, animate, true);
	}

	private int getItemPosition(T item) {
		return item != null && mData != null && !mData.isEmpty() ? mData.indexOf(item) : -1;
	}

	private boolean hasSubItems(IExpandable item) {
		List list = item.getSubItems();
		return list != null && list.size() > 0;
	}

	public boolean isExpandable(T item) {
		return item != null && item instanceof IExpandable;
	}

	private IExpandable getExpandableItem(int position) {
		T item = getItem(position);
		if (isExpandable(item)) {
			return (IExpandable) item;
		} else {
			return null;
		}
	}

	/**
	 * Get the parent item position of the IExpandable item
	 *
	 * @return return the closest parent item position of the IExpandable.
	 * if the IExpandable item's level is 0, return itself position.
	 * if the item's level is negative which mean do not implement this, return a negative
	 * if the item is not exist in the data list, return a negative.
	 */
	public int getParentPosition(@NonNull T item) {
		int position = getItemPosition(item);
		if (position == -1) {
			return -1;
		}

		// if the item is IExpandable, return a closest IExpandable item position whose level smaller than this.
		// if it is not, return the closest IExpandable item position whose level is not negative
		int level;
		if (item instanceof IExpandable) {
			level = ((IExpandable) item).getLevel();
		} else {
			level = Integer.MAX_VALUE;
		}
		if (level == 0) {
			return position;
		} else if (level == -1) {
			return -1;
		}

		for (int i = position; i >= 0; i--) {
			T temp = mData.get(i);
			if (temp instanceof IExpandable) {
				IExpandable expandable = (IExpandable) temp;
				if (expandable.getLevel() >= 0 && expandable.getLevel() < level) {
					return i;
				}
			}
		}
		return -1;
	}

	/**
	 * Interface definition for a callback to be invoked when an itemchild in this
	 * view has been clicked
	 */
	public interface OnItemChildClickListener {
		/**
		 * callback method to be invoked when an item in this view has been
		 * click and held
		 *
		 * @param view     The view whihin the ItemView that was clicked
		 * @param position The position of the view int the adapter
		 * @return true if the callback consumed the long click ,false otherwise
		 */
		boolean onItemChildClick(BaseQuickAdapter adapter, View view, int position);
	}


	/**
	 * Interface definition for a callback to be invoked when an childView in this
	 * view has been clicked and held.
	 */
	public interface OnItemChildLongClickListener {
		/**
		 * callback method to be invoked when an item in this view has been
		 * click and held
		 *
		 * @param view     The childView whihin the itemView that was clicked and held.
		 * @param position The position of the view int the adapter
		 * @return true if the callback consumed the long click ,false otherwise
		 */
		void onItemChildLongClick(BaseQuickAdapter adapter, View view, int position);
	}

	/**
	 * Interface definition for a callback to be invoked when an item in this
	 * view has been clicked and held.
	 */
	public interface OnItemLongClickListener {
		/**
		 * callback method to be invoked when an item in this view has been
		 * click and held
		 *
		 * @param adapter  the adpater
		 * @param view     The view whihin the RecyclerView that was clicked and held.
		 * @param position The position of the view int the adapter
		 * @return true if the callback consumed the long click ,false otherwise
		 */
		boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position);
	}


	/**
	 * Interface definition for a callback to be invoked when an item in this
	 * RecyclerView itemView has been clicked.
	 */
	public interface OnItemClickListener {

		/**
		 * Callback method to be invoked when an item in this RecyclerView has
		 * been clicked.
		 *
		 * @param adapter  the adpater
		 * @param view     The itemView within the RecyclerView that was clicked (this
		 *                 will be a view provided by the adapter)
		 * @param position The position of the view in the adapter.
		 */
		void onItemClick(BaseQuickAdapter adapter, View view, int position);
	}

	/**
	 * Register a callback to be invoked when an item in this RecyclerView has
	 * been clicked.
	 *
	 * @param listener The callback that will be invoked.
	 */
	public void setOnItemClickListener(@Nullable OnItemClickListener listener) {
		mOnItemClickListener = listener;
	}

	/**
	 * Register a callback to be invoked when an itemchild in View has
	 * been  clicked
	 *
	 * @param listener The callback that will run
	 */
	public void setOnItemChildClickListener(OnItemChildClickListener listener) {
		mOnItemChildClickListener = listener;
	}

	/**
	 * Register a callback to be invoked when an item in this RecyclerView has
	 * been long clicked and held
	 *
	 * @param listener The callback that will run
	 */
	public void setOnItemLongClickListener(OnItemLongClickListener listener) {
		mOnItemLongClickListener = listener;
	}

	/**
	 * Register a callback to be invoked when an itemchild  in this View has
	 * been long clicked and held
	 *
	 * @param listener The callback that will run
	 */
	public void setOnItemChildLongClickListener(OnItemChildLongClickListener listener) {
		mOnItemChildLongClickListener = listener;
	}


	/**
	 * @return The callback to be invoked with an item in this RecyclerView has
	 * been long clicked and held, or null id no callback as been set.
	 */
	public final OnItemLongClickListener getOnItemLongClickListener() {
		return mOnItemLongClickListener;
	}

	/**
	 * @return The callback to be invoked with an item in this RecyclerView has
	 * been clicked and held, or null id no callback as been set.
	 */
	public final OnItemClickListener getOnItemClickListener() {
		return mOnItemClickListener;
	}

	/**
	 * @return The callback to be invoked with an itemchild in this RecyclerView has
	 * been clicked, or null id no callback has been set.
	 */
	@Nullable
	public final OnItemChildClickListener getOnItemChildClickListener() {
		return mOnItemChildClickListener;
	}

	/**
	 * @return The callback to be invoked with an itemChild in this RecyclerView has
	 * been long clicked, or null id no callback has been set.
	 */
	@Nullable
	public final OnItemChildLongClickListener getmOnItemChildLongClickListener() {
		return mOnItemChildLongClickListener;
	}
}
